﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Medora.Core;
using Medora.DataContract;
using Medora.Model;
using Medora.Model.Basic;
using Translator = Medora.Services.Basic.Translator;

namespace Medora.Services.Utils
{
    public class ServiceBase<T, F> : IService<T, F> where T: DeletedEntity where  F: IFilter
    {
        protected IUow _uow;
        protected IRepository<T> _repository;
        private IUserService _userService;
        protected IFilterConstructor<F, T> _filterConstructor;

        public ServiceBase(IUow uow, IUserService userService, IFilterConstructor<F, T> filterConstructor)
        {
            _uow = uow;
            _userService = userService;
            _filterConstructor = filterConstructor;
        }

        public IPagedEntities<T> GetPaged(int pageSize, int page, F filter, SortInfo sortInfo = null)
        {

            var finalE = GetFilter(filter);
            if (sortInfo == null || string.IsNullOrEmpty(sortInfo.Field))
            {
                sortInfo = new SortInfo { Direction = SortOrderType.Desc, Field = "CreatedOn" };
            }

            var entities = _repository.GetTake(pageSize, page, finalE, sortInfo).ToList();


            return new PagedEntities<T>
                {
                    Entities = entities,
                    Count = _repository.GetCount(finalE)
                };

        }

        public IEnumerable<T> Get(F filter)
        {
            var finalE = GetFilter(filter);
            return _repository.Get(finalE);
        }

        public T GetById(Guid id)
        {
            return _repository.GetById(id);
        }

        private Expression<Func<T, bool>> GetFilter(F filter)
        {
            Expression<Func<T, bool>> startE = o => o.Deleted != true;
            return ConstructFilter(startE, filter);
        }

        public int GetCount()
        {
            Expression<Func<T, bool>> startE = o => o.Deleted != true;
            return _repository.GetCount(startE);
        }

        public bool Save(Guid userId, T obj)
        {
            //todo add logging on save
            try
            {
                if (obj.Id != Guid.Empty)
                {
                    _repository.Update(obj);
                }
                else
                {
                    obj.CreatedBy = userId;
                    obj.CreatedOn = DateTime.Now;

                    _repository.Add(obj);
                }
                _uow.Commit();
                return true;
            }
            catch (Exception e)
            {
                Logger.WriteLog(e);
                throw new Exception(Translator.Instance.Translate("Error during data save"));
            }
        }

        public bool Delete(List<Guid> ids)
        {
            try
            {
                var objs = _repository.Get(o => ids.Contains(o.Id)).ToList();
                foreach (var obj in objs)
                {
                    obj.Deleted = true;
                    _repository.OneFieldUpdate(obj, d => d.Deleted);
                }
                _uow.Commit();
                return true;
            }
            catch (Exception e)
            {
                Logger.WriteLog(e);
                throw new Exception(Translator.Instance.Translate("Error during data save"));
            }
        }

        protected virtual Expression<Func<T, bool>> ConstructFilter(Expression<Func<T, bool>> startE, F filter)
        {
            if (filter == null) return startE;
            return _filterConstructor.GetFilter(filter, startE) ?? startE;
        }
    }
}